| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- 'use client';
- import { use, useEffect, useRef, useState } from 'react';
- import { useDonationHub } from '@/hooks/useDonationHub';
- import { fetchApi } from '@/lib/utils/client';
- import './style.scss';
- type Props = {
- params: Promise<{ widgetToken: string }>;
- searchParams: Promise<{ [key: string]: string|string[]|undefined }>;
- };
- type RankListResponse = {
- list: { rank: number; sponsorMemberID: number; sponsorName: string; totalAmount: number; donationCount: number }[];
- };
- type RankStyleConfig = {
- id: number;
- title: string;
- theme: number;
- period: number;
- isShowAmount: boolean;
- maxRankCount: number;
- titleFontFamily: string|null;
- titleFontSizePx: number;
- titleFontColor: string;
- rank1FontFamily: string|null;
- rank1FontSizePx: number;
- rank1FontColor: string;
- rank2FontFamily: string|null;
- rank2FontSizePx: number;
- rank2FontColor: string;
- rank3FontFamily: string|null;
- rank3FontSizePx: number;
- rank3FontColor: string;
- isShowDonationCount: boolean;
- isActive: boolean;
- };
- export default function RankPage({ params, searchParams }: Props) {
- const { widgetToken } = use(params);
- const sp = use(searchParams);
- const configID = typeof sp.configID === 'string' ? parseInt(sp.configID, 10) : null;
- const hubUrl = process.env.NEXT_PUBLIC_API_URL + '/hubs/donation';
- const { ranking, setRanking } = useDonationHub(widgetToken, hubUrl);
- const [styleCfg, setStyleCfg] = useState<RankStyleConfig|null>(null);
- const widgetRef = useRef<HTMLDivElement>(null);
- // 스타일 config 로드
- useEffect(() => {
- const qs = configID ? `?configID=${configID}` : '';
- fetchApi<RankStyleConfig>(`/api/widget/rank/config/${widgetToken}${qs}`, { silent: true }).then(res => {
- if (res.data) {
- setStyleCfg(res.data);
- }
- }).catch(() => {});
- }, [widgetToken, configID]);
- // 순위 데이터 로드
- useEffect(() => {
- const qs = configID ? `?configID=${configID}` : '';
- fetchApi<RankListResponse>(`/api/widget/rank/by-token/${widgetToken}${qs}`, { silent: true }).then(res => {
- if (res.data?.list) {
- setRanking(res.data.list);
- }
- }).catch(() => {});
- }, [widgetToken, configID, setRanking]);
- // CSS variables 적용
- useEffect(() => {
- if (!widgetRef.current) {
- return;
- }
- const el = widgetRef.current;
- el.style.setProperty('--rank-title-font-family', styleCfg?.titleFontFamily ?? 'inherit');
- el.style.setProperty('--rank-title-font-size', `${styleCfg?.titleFontSizePx ?? 18}px`);
- el.style.setProperty('--rank-title-color', styleCfg?.titleFontColor ?? '#FFFFFF');
- el.style.setProperty('--rank-1-font-family', styleCfg?.rank1FontFamily ?? 'inherit');
- el.style.setProperty('--rank-1-font-size', `${styleCfg?.rank1FontSizePx ?? 15}px`);
- el.style.setProperty('--rank-1-color', styleCfg?.rank1FontColor ?? '#FFD700');
- el.style.setProperty('--rank-2-font-family', styleCfg?.rank2FontFamily ?? 'inherit');
- el.style.setProperty('--rank-2-font-size', `${styleCfg?.rank2FontSizePx ?? 15}px`);
- el.style.setProperty('--rank-2-color', styleCfg?.rank2FontColor ?? '#C0C0C0');
- el.style.setProperty('--rank-3-font-family', styleCfg?.rank3FontFamily ?? 'inherit');
- el.style.setProperty('--rank-3-font-size', `${styleCfg?.rank3FontSizePx ?? 15}px`);
- el.style.setProperty('--rank-3-color', styleCfg?.rank3FontColor ?? '#CD7F32');
- }, [styleCfg]);
- const title = styleCfg?.title ?? '후원 순위';
- const isShowAmount = styleCfg?.isShowAmount ?? true;
- const isShowCount = styleCfg?.isShowDonationCount ?? false;
- return (
- <div ref={widgetRef} className="rank-widget">
- <div className="rank-title">{title}</div>
- <div className="rank-list">
- {ranking.map(item => {
- const badgeClass = item.rank <= 3 ? `badge-${item.rank}` : 'badge-default';
- return (
- <div key={`${item.rank}-${item.sponsorMemberID}`} className={`rank-item rank-${item.rank}`}>
- <div className={`rank-badge ${badgeClass}`}>{item.rank}</div>
- <div className="rank-name">{item.sponsorName}</div>
- {isShowAmount && (
- <div className="rank-amount">
- {item.totalAmount.toLocaleString()}원
- {isShowCount && ` (${item.donationCount}회)`}
- </div>
- )}
- </div>
- );
- })}
- </div>
- </div>
- );
- }
|